/****************************************************************************** * Copyright (c) 2002, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.runtime.diagram.ui.printing.internal.printpreview; import java.util.ArrayList; import java.util.List; import javax.print.PrintService; import javax.print.PrintServiceLookup; import org.eclipse.core.runtime.Assert; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.SWTGraphics; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.LayerConstants; import org.eclipse.gef.RootEditPart; import org.eclipse.gmf.runtime.common.core.util.Log; import org.eclipse.gmf.runtime.common.core.util.Trace; import org.eclipse.gmf.runtime.common.ui.action.actions.IPrintActionHelper; import org.eclipse.gmf.runtime.common.ui.util.WindowUtil; import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IDiagramPreferenceSupport; import org.eclipse.gmf.runtime.diagram.ui.internal.editparts.PageBreakEditPart; import org.eclipse.gmf.runtime.diagram.ui.internal.figures.PageBreaksFigure; import org.eclipse.gmf.runtime.diagram.ui.internal.pagesetup.PageInfoHelper; import org.eclipse.gmf.runtime.diagram.ui.internal.pagesetup.PageInfoHelper.PageMargins; import org.eclipse.gmf.runtime.diagram.ui.internal.properties.WorkspaceViewerProperties; import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor; import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramGraphicalViewer; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramGraphicalViewer; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.DiagramPrintingDebugOptions; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.DiagramPrintingPlugin; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.DiagramPrintingStatusCodes; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.l10n.DiagramUIPrintingMessages; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.l10n.DiagramUIPrintingPluginImages; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.util.HeaderAndFooterHelper; import org.eclipse.gmf.runtime.diagram.ui.printing.internal.util.PrintHelperUtil; import org.eclipse.gmf.runtime.draw2d.ui.internal.graphics.MapModeGraphics; import org.eclipse.gmf.runtime.draw2d.ui.internal.graphics.ScaledGraphics; import org.eclipse.gmf.runtime.draw2d.ui.internal.mapmode.DiagramMapModeUtil; import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; import org.eclipse.gmf.runtime.draw2d.ui.render.internal.graphics.RenderedMapModeGraphics; import org.eclipse.gmf.runtime.draw2d.ui.render.internal.graphics.RenderedScaledGraphics; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; /** * Print Preview Action to display the Print Preview dialog. There are no static * methods, so you must create an instance of this class. * * Call doPrintPreview() after you've made an instance. * * This class should be combined with the DiagramPrinter to reuse functionality. * * @author Wayne Diu, wdiu */ public class PrintPreviewHelper{ /** * Action helper for print. This must be passed in to have something happen * when print is pressed */ protected IPrintActionHelper printActionHelper; /** * Increment userX everytime the user moves right, decrement userX everytime * the user moves left. * * userX >= 0 */ protected int userX = 0; /** * Increment userY everytime the user moves down, decrement userY everytime * the user moves up. * * userY >= 0 */ protected int userY = 0; /** * Number of rows, initialized with initial number of rows of pages to * display */ protected int numberOfRowsToDisplay = 2; /** * Number of columns, initialized with initial number of columns of pages to * display */ protected int numberOfColumnsToDisplay = 2; /** * The diagram edit part */ protected DiagramEditPart diagramEditPart; /** * Max bounds of a page for no page break */ protected Rectangle pageBreakBounds; /* SWT interface variables */ /** * Body of the shell. */ protected Composite body; /** * Composite for the pages */ protected Composite composite; /** * Height of the button bar, initialized right before the button bar is * created. */ protected int buttonBarHeight; /** * Shell for the new pop up */ protected Shell shell; /** * Temporary shell to be used when creating the diagram editpart. */ private Shell tempShell; /* Toolbar items are in left to right order */ /** * Print item on toolbar */ protected ToolItem printTool; /** * Enable or disable the print option */ protected boolean enablePrinting = true; /** * Pages item on toolbar */ protected ToolItem pagesTool; /** * Left item on toolbar */ protected ToolItem leftTool; /** * Right item on toolbar */ protected ToolItem rightTool; /** * Up item on toolbar */ protected ToolItem upTool; /** * Down item on toolbar */ protected ToolItem downTool; /** * Close item on toolbar */ protected ToolItem closeTool; /** * It's easiest to keep track of the page images using an image list, but I * could also have done getImage on the labels */ private List imageList = new ArrayList(); /** * Border size */ protected static final int BORDER_SIZE = 20; /** * the background color */ private static final Color BACKGROUND_COLOR = new Color(Display .getDefault(), 124, 124, 124); /* Images */ /** * Enabled print image */ protected Image printImage; /** * Disabled Print image */ protected Image disabledPrintImage; /** * Page image, unlikely to ever be disabled */ protected Image pageImage; /** * Enabled left image */ protected Image leftImage; /** * Disabled left image */ protected Image disabledLeftImage; /** * Enabled right image */ protected Image rightImage; /** * Disabled right image */ protected Image disabledRightImage; /** * Enabled up image */ protected Image upImage; /** * Diabled up image */ protected Image disabledUpImage; /** * Enabled down image */ protected Image downImage; /** * Disabled down image */ protected Image disabledDownImage; /** * Close image, unlikely to ever be disabled */ protected Image closeImage; /** * The print preview helper is capable of showing zoom input. * userScale is a value between 0 and 1. */ protected double userScale = 1; /** * Determine if we should consider fit to page options or not. */ private boolean fitToPage = false; /** * Initial zoom levels. */ private int[] zoomLevels = { 25, 50, 75, 100, 150, 200, 250, 300, 400 }; /** * A constant used in the combo box for scaling. */ private static final String FIT_TO_PAGES = DiagramUIPrintingMessages.PrintPreview_FitToPage_ButtonText; /** * A minimum scale percentage. */ private static int MINIMUM_SCALE_FACTOR = 5; /** * Initialize all toolbar images */ protected void initializeToolbarImages() { printImage = DiagramUIPrintingPluginImages.DESC_PRINT.createImage(); disabledPrintImage = DiagramUIPrintingPluginImages.DESC_PRINT_DISABLED .createImage(); pageImage = DiagramUIPrintingPluginImages.DESC_PAGE.createImage(); leftImage = DiagramUIPrintingPluginImages.DESC_LEFT.createImage(); disabledLeftImage = DiagramUIPrintingPluginImages.DESC_LEFT_DISABLED .createImage(); rightImage = DiagramUIPrintingPluginImages.DESC_RIGHT.createImage(); disabledRightImage = DiagramUIPrintingPluginImages.DESC_RIGHT_DISABLED .createImage(); upImage = DiagramUIPrintingPluginImages.DESC_UP.createImage(); disabledUpImage = DiagramUIPrintingPluginImages.DESC_UP_DISABLED .createImage(); downImage = DiagramUIPrintingPluginImages.DESC_DOWN.createImage(); disabledDownImage = DiagramUIPrintingPluginImages.DESC_DOWN_DISABLED .createImage(); closeImage = DiagramUIPrintingPluginImages.DESC_CLOSE.createImage(); } /** * Enable or disable printing depending on where the print preview is invoked from. * * @param enablePrinting */ public void enablePrinting(boolean enablePrinting){ this.enablePrinting = enablePrinting; } /** * Do the print preview. * * @param printActionHelper, * an IPrintActionHelper with a doPrint method that will be * called when the print button is pressed. I use this parameter * so that this class can be extensible, e.g. * ModelerPrintActionHelper will implement it for Modeler and * something else will implement it for a different editor. */ public void doPrintPreview(IPrintActionHelper prActionHelper) { this.printActionHelper = prActionHelper; if (!fitToPage) { setUserScale(PrintHelperUtil.getScale()); } if (getDiagramEditorPart() == null) { MessageDialog .openInformation(PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell(), DiagramUIPrintingMessages.PrintPreview_Title, DiagramUIPrintingMessages.PrintPreview_NotEnabled); return; } if (!isPrinterInstalled()) { WindowUtil .doMessageBox(DiagramUIPrintingMessages.PrintPreview_NoPrinterInstalled, DiagramUIPrintingMessages.PrintPreview_Title, SWT.ICON_ERROR, PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell()); return; } initializeToolbarImages(); initializeMapMode(); diagramEditPart = null; pageBreakBounds = null; userX = 0; userY = 0; if (getTotalNumberOfRows() == 1 && getTotalNumberOfColumns() == 1) { numberOfRowsToDisplay = 1; numberOfColumnsToDisplay = 1; } else if (getTotalNumberOfRows() == 1) { numberOfRowsToDisplay = 1; numberOfColumnsToDisplay = 2; } else { numberOfRowsToDisplay = 2; numberOfColumnsToDisplay = 2; } Display display = PlatformUI.getWorkbench().getDisplay(); //check for rtl Torientation... int style = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().getStyle(); if ((style & SWT.MIRRORED) != 0) { shell = new Shell(display, SWT.APPLICATION_MODAL | SWT.TITLE | SWT.CLOSE | SWT.BORDER | SWT.RIGHT_TO_LEFT); } else shell = new Shell(display, SWT.APPLICATION_MODAL | SWT.TITLE | SWT.CLOSE | SWT.BORDER); shell.setSize(PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getShell().getSize()); shell.setText(DiagramUIPrintingMessages.PrintPreview_Title); shell.setLocation(PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getShell().getLocation()); shell.setLayout(new GridLayout(1, true)); ToolBar bar = new ToolBar(shell, SWT.FLAT | SWT.HORIZONTAL); printTool = new ToolItem(bar, SWT.NULL); printTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_PrintToolItem); printTool.setImage(printImage); printTool.setDisabledImage(disabledPrintImage); printTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { //should not be enabled Assert.isNotNull(printActionHelper); printActionHelper .doPrint(PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getActivePage() .getActivePart()); shell.setActive(); } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); if (printActionHelper == null || !enablePrinting) { printTool.setEnabled(false); } new ToolItem(bar, SWT.SEPARATOR); pagesTool = new ToolItem(bar, SWT.DROP_DOWN); pagesTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_PagesToolItem); pagesTool.setImage(pageImage); pagesTool.addSelectionListener(new SelectionAdapter() { private Menu menu = null; //also update userX, userY, numberOfRowsToDisplay, // numberOfColumnsToDisplay private void updatePreview(int newNumberOfColumnsToDisplay, int newNumberOfRowsToDisplay) { numberOfRowsToDisplay = newNumberOfRowsToDisplay; numberOfColumnsToDisplay = newNumberOfColumnsToDisplay; //When switching the number of pages to display to a bigger //number, you can get an extra blank page on the right or on //the bottom. This check prevents the extra blank page. if (userX + numberOfColumnsToDisplay > getTotalNumberOfColumns()) { //move it left userX = getTotalNumberOfColumns() - numberOfColumnsToDisplay; //be safe, check for 0 if (userX < 0) userX = 0; } if (userY + numberOfRowsToDisplay > getTotalNumberOfRows()) { //move it up userY = getTotalNumberOfRows() - numberOfRowsToDisplay; //be safe, check for 0 if (userY < 0) userY = 0; } refreshComposite(); } public void widgetSelected(SelectionEvent event) { // Create the menu if it has not already been created if (menu == null) { // Lazy create the menu. menu = new Menu(shell); MenuItem menuItem = new MenuItem(menu, SWT.NONE); menuItem.setText(DiagramUIPrintingMessages.PrintPreview_1Up); menuItem.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { updatePreview(1, 1); } }); menuItem = new MenuItem(menu, SWT.NONE); menuItem.setText(DiagramUIPrintingMessages.PrintPreview_2Up); menuItem.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { updatePreview(2, 1); } }); menuItem = new MenuItem(menu, SWT.NONE); menuItem.setText(DiagramUIPrintingMessages.PrintPreview_4Up); menuItem.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { updatePreview(2, 2); } }); } final ToolItem toolItem = (ToolItem) event.widget; final ToolBar toolBar = toolItem.getParent(); org.eclipse.swt.graphics.Rectangle toolItemBounds = toolItem .getBounds(); //left aligned under the pages button Point point = toolBar.toDisplay(new Point(toolItemBounds.x, toolItemBounds.y)); menu.setLocation(point.x, point.y + toolItemBounds.height); setMenuVisible(true); } private void setMenuVisible(boolean visible) { menu.setVisible(visible); } }); new ToolItem(bar, SWT.SEPARATOR); leftTool = new ToolItem(bar, SWT.NULL); if ((style & SWT.MIRRORED) != 0) { //switch left and right for RTL... leftTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_RightToolItem); leftTool.setImage(rightImage); leftTool.setDisabledImage(disabledRightImage); } else { leftTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_LeftToolItem); leftTool.setImage(leftImage); leftTool.setDisabledImage(disabledLeftImage); } leftTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { if (userX > 0) { userX--; refreshComposite(); } } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); rightTool = new ToolItem(bar, SWT.NULL); if ((style & SWT.MIRRORED) != 0) { //switch left and right for RTL rightTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_LeftToolItem); rightTool.setImage(leftImage); rightTool.setDisabledImage(disabledLeftImage); } else { rightTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_RightToolItem); rightTool.setImage(rightImage); rightTool.setDisabledImage(disabledRightImage); } rightTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { //check for max pages to be safe if (!(userX + numberOfColumnsToDisplay + 1 > getTotalNumberOfColumns())) { userX++; refreshComposite(); } } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); upTool = new ToolItem(bar, SWT.NULL); upTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_UpToolItem); upTool.setImage(upImage); upTool.setDisabledImage(disabledUpImage); upTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { if (userY > 0) { userY--; refreshComposite(); } } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); downTool = new ToolItem(bar, SWT.NULL); downTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_DownToolItem); downTool.setImage(downImage); downTool.setDisabledImage(disabledDownImage); downTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { if (!(userY + numberOfRowsToDisplay + 1 > getTotalNumberOfRows())) { userY++; refreshComposite(); } } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); new ToolItem(bar, SWT.SEPARATOR); ToolItem separator = new ToolItem(bar, SWT.SEPARATOR); final Combo zoomCombo = new Combo(bar, SWT.DROP_DOWN); zoomCombo.add(FIT_TO_PAGES); for (int i = 0; i < zoomLevels.length; i++) { zoomCombo.add(getDisplayScale(zoomLevels[i])); } if (this.fitToPage) { zoomCombo.setText(FIT_TO_PAGES); zoomCombo.select(0); } else { zoomCombo.setText(getDisplayScale(PrintHelperUtil.getScale())); } zoomCombo.addSelectionListener(new SelectionAdapter() { /** * Apply the zoom scale as indicated by the text in the combo-box selection. * * @param combo Obtain zoom information from the combo-box. */ private void doZoom(Combo combo) { String scaleFactor = combo.getText(); int percentageIndex = scaleFactor.indexOf("%"); //$NON-NLS-1$ if (percentageIndex > 0) { scaleFactor = scaleFactor.substring(0, percentageIndex); } int scalePercentage = PrintHelperUtil.getScale(); try { scalePercentage = Integer.parseInt(scaleFactor); } catch (NumberFormatException e) { // Ignore invalid entry; default is last known acceptable value } if(scalePercentage < MINIMUM_SCALE_FACTOR){ scalePercentage = MINIMUM_SCALE_FACTOR; } setPercentScaling(scalePercentage); refreshComposite(); combo.setText(getDisplayScale(scalePercentage)); } /* * (non-Javadoc) * * @see org.eclipse.swt.events.SelectionAdapter#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { doZoom((Combo) e.getSource()); } /* * (non-Javadoc) * * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetSelected(SelectionEvent e) { String selectedString = ((Combo) e.getSource()).getText(); if (FIT_TO_PAGES.compareToIgnoreCase(selectedString) == 0) { FitToPagesDialog fitToPages = new FitToPagesDialog(shell); if (fitToPages.open() == Dialog.OK) { int pagesWide = fitToPages.getPagesWide(); int pagesTall = fitToPages.getPagesTall(); PrintHelperUtil.setScaleToWidth(pagesWide); PrintHelperUtil.setScaleToHeight(pagesTall); setFitToPage(pagesWide, pagesTall); refreshComposite(); } } else { doZoom((Combo) e.getSource()); } } }); zoomCombo.pack(); separator.setWidth(zoomCombo.getSize().x); separator.setControl(zoomCombo); new ToolItem(bar, SWT.SEPARATOR); closeTool = new ToolItem(bar, SWT.NULL); closeTool.setToolTipText(DiagramUIPrintingMessages.PrintPreview_CloseToolItem); closeTool.setImage(closeImage); closeTool.addSelectionListener(new SelectionListener() { /** * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) */ public void widgetSelected(SelectionEvent e) { dispose(); shell.close(); shell.dispose(); } /** * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { widgetSelected(e); } }); buttonBarHeight = bar.getBounds().height - bar.getBounds().y; bar.setBounds(0, 0, shell.getSize().x, buttonBarHeight); //do the body in the middle body = new Composite(shell, SWT.NULL); body.setLayout(new GridLayout(1, true)); body.setLayoutData(new GridData(GridData.FILL_BOTH)); body.setBackground(BACKGROUND_COLOR); composite = new Composite(body, SWT.NULL); composite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); refreshComposite(); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } dispose(); shell.dispose(); } private IMapMode mm; /** * The constructor. */ public PrintPreviewHelper() { //do nothing } /** * @return <code>IMapMode</code> to do the coordinate mapping */ protected IMapMode getMapMode() { return mm; } /** * Initialize the map mode variable */ private void initializeMapMode() { DiagramEditor diagramEditor = getDiagramEditorPart(); assert diagramEditor != null; IDiagramGraphicalViewer viewer = diagramEditor.getDiagramGraphicalViewer(); if (viewer != null) { RootEditPart rootEP = viewer.getRootEditPart(); if (rootEP instanceof DiagramRootEditPart) { this.mm = ((DiagramRootEditPart) rootEP).getMapMode();; return; } } this.mm = MapModeUtil.getMapMode(); } /** * Return and cache the total number of rows used by the diagram * * @return int total number of rows used by the diagram */ private int getTotalNumberOfRows() { float numRows = ((float) (getBounds().height * userScale)) / PageInfoHelper .getPageSize(getPreferenceStore(), true, getMapMode()).y; return Math.max(1, (int) Math.ceil(numRows)); } /** * Return the diagram editor part that we are doing the print preview for * * @return DiagramEditor, diagram editor part that we are doing the print * preview for. I do not return IDiagramWorkbenchPart because * DiagramEditorPart contains some extra methods for the page break * checks. */ private DiagramEditor getDiagramEditorPart() { //more explicit than using window IEditorPart editorPart = PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getActivePage().getActiveEditor(); if (!(editorPart instanceof DiagramEditor)) { return null; } DiagramEditor diagramEditorPart = (DiagramEditor) editorPart; return diagramEditorPart; } /** * Return the diagram bounds of the diagram we are doing the print preview * for. * * @return Rectangle with diagram bounds of the diagram we are doing the * print preview for. */ protected Rectangle getDiagramBounds() { return PageInfoHelper.getChildrenBounds(getDiagramEditPart(), null); //null because we are not ignoring anything } /** * Return and cache the page break bounds of the diagram we are doing the * print preview for. * * @return Rectangle with page break bounds of the diagram we are doing the * print preview for. */ protected Rectangle getPageBreakBounds() { if (pageBreakBounds == null) { pageBreakBounds = PrintHelperUtil.getPageBreakBounds(getDiagramEditPart(), true).getCopy(); } return pageBreakBounds; } /** * Return the pageBreakBounds if possible. If not, return the diagram * bounds. * * @return Rectangle with page break bounds if possible. If not possible, * just return the diagram bounds. */ protected Rectangle getBounds() { //don't worry about storing it, it's cached return (getPageBreakBounds() == null) ? getDiagramBounds() : getPageBreakBounds(); } /** * Return the diagram edit part that we are doing the print preview for. * Uses getDiagramEditorPart(). * * @return DiagramEditPart, diagram edit part that we are doing the print * preview for */ protected DiagramEditPart getDiagramEditPart() { if (diagramEditPart == null) { diagramEditPart = getDiagramEditorPart().getDiagramEditPart(); } if (diagramEditPart == null) { Diagram diagram = getDiagramEditorPart().getDiagram(); //do not getDiagramEditPart PreferencesHint preferencesHint = getPreferencesHint(getDiagramEditorPart()); diagramEditPart = PrintHelperUtil.createDiagramEditPart(diagram, preferencesHint, getTempShell()); PrintHelperUtil.initializePreferences(diagramEditPart, preferencesHint); } return diagramEditPart; } /** * Lazily creates a new shell. * @return */ private Shell getTempShell() { if (tempShell == null) { tempShell = new Shell(); } return tempShell; } protected PreferencesHint getPreferencesHint(IEditorPart editorPart) { if (editorPart instanceof IDiagramWorkbenchPart) { RootEditPart rootEP = ((IDiagramWorkbenchPart) editorPart) .getDiagramGraphicalViewer().getRootEditPart(); if (rootEP instanceof IDiagramPreferenceSupport) { return ((IDiagramPreferenceSupport) rootEP) .getPreferencesHint(); } } return PreferencesHint.USE_DEFAULTS; } /** * Return and cache the total number of columns used by the diagram * * @return int total number of columns used by the diagram */ private int getTotalNumberOfColumns() { float numCols = ((float) (getBounds().width * userScale)) / PageInfoHelper .getPageSize(getPreferenceStore(), true, getMapMode()).x; return Math.max(1, (int) Math.ceil(numCols)); } /** * Make sure printer is installed. Should not be able to print preview if no * printer is installed, even though technically it will work. * * Call this immediately with the rest of the initialization. */ protected boolean isPrinterInstalled() { try { PrintService[] printServices = PrintServiceLookup.lookupPrintServices( null, null); return printServices.length > 0; } catch (SWTError e) { Trace.catching(DiagramPrintingPlugin.getInstance(), DiagramPrintingDebugOptions.EXCEPTIONS_CATCHING, PrintPreviewHelper.class, "isPrinterInstalled", //$NON-NLS-1$ e); if (e.code == SWT.ERROR_NO_HANDLES) { //it might have really been ERROR_NO_HANDLES, but there's //no way for me to really know return false; } //if (e.code != SWT.ERROR_NO_HANDLES) Log.error(DiagramPrintingPlugin.getInstance(), DiagramPrintingStatusCodes.GENERAL_UI_FAILURE, "Failed to make instance of Printer object", e); //$NON-NLS-1$ //else if another swt error Trace.throwing(DiagramPrintingPlugin.getInstance(), DiagramPrintingDebugOptions.EXCEPTIONS_CATCHING, PrintPreviewHelper.class, "isPrinterInstalled", //$NON-NLS-1$ e); throw e; } } /** * Clean up by deleting the images in the image list */ private void disposeImages() { while (imageList.size() > 0) { Assert.isTrue(imageList.get(0) instanceof Image); if (!((Image) imageList.get(0)).isDisposed()) ((Image) imageList.get(0)).dispose(); imageList.remove(0); } } /** * A convenience method for refreshing the displayed image in the preview. */ private void refreshComposite(){ updateCompositeForNumberOfColumns(numberOfRowsToDisplay, numberOfColumnsToDisplay); updateLeftRightUpDownButtonsForToolbar(); } /** * Draw the composite centered on the body based on the number of columns. * Also calls the method to make the images and insert them into the * composite. * * @param numberOfRows * the number of rows that the composite should contain. I need * this to figure out the height of the image. * @param numberOfColumns * the number of columns that the composite should contain. I * need this to figure out the width of the image. */ private void updateCompositeForNumberOfColumns(int numberOfRows, int numberOfColumns) { Assert.isNotNull(shell); Assert.isNotNull(composite); WindowUtil.disposeChildren(composite); disposeImages(); //the next two lines of code are intentional composite.setLayout(null); composite.pack(); composite.setLayout(new GridLayout(numberOfColumns, true)); //(shell height - toolbar height - top border - bottom border - title - // ((# of rows - 1) x vertical border between images)) / # of rows int imageHeight = (shell.getSize().y - buttonBarHeight - BORDER_SIZE - BORDER_SIZE - BORDER_SIZE - ((numberOfRows - 1) * BORDER_SIZE)) / numberOfRows; //(shell width - left border - right border - ((# of columns - 1) x // horizontal border between images)) / # of columns int imageWidth = (shell.getSize().x - BORDER_SIZE - BORDER_SIZE - ((numberOfColumns - 1) * BORDER_SIZE)) / numberOfColumns; //now adjust to the limiting one based on aspect ratio //to make this conform to the page breaks, RATLC00247228 //get printer ratio from the page, not the real printer org.eclipse.draw2d.geometry.Point pageSize = PageInfoHelper .getPageSize(getPreferenceStore(), false, getMapMode()); Assert.isNotNull(pageSize); //width / height float printerRatio = ((float) pageSize.x) / ((float) pageSize.y); if (imageHeight * printerRatio < imageWidth) { //round down imageWidth = (int) (imageHeight * printerRatio); } else if (imageWidth * (1 / printerRatio) < imageHeight) { //round down imageHeight = (int) (imageWidth * (1.0f / printerRatio)); } PageMargins margins = PageInfoHelper.getPageMargins(getPreferenceStore(), getMapMode()); //make sure height and width are not 0, if too small <4, don't bother if (!(imageHeight <= 4 || imageWidth <= 4)) { //or imageWidth / pageSize.x float scale = ( imageHeight / (float) pageSize.y) / (float) DiagramMapModeUtil.getScale(getMapMode()); scale *= userScale; margins.left /= userScale; margins.right /= userScale; margins.bottom /= userScale; margins.top /= userScale; for (int i = 0; i < numberOfRows; i++) { for (int j = 0; j < numberOfColumns; j++) { Label label = new Label(composite, SWT.NULL); Image pageImg = makeImage(imageWidth, imageHeight, i, j, scale, margins); label.setImage(pageImg); imageList.add(pageImg); } } } composite.pack(); //GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_CENTER // won't do it for you org.eclipse.swt.graphics.Rectangle compositeBounds = composite .getBounds(); //this approximation is OK compositeBounds.x = (shell.getSize().x - BORDER_SIZE - compositeBounds.width) / 2; compositeBounds.y = (shell.getSize().y - buttonBarHeight - BORDER_SIZE - BORDER_SIZE - BORDER_SIZE - compositeBounds.height) / 2; composite.setBounds(compositeBounds); } /** * Update the enabled and disabled states for the toolbar */ protected void updateLeftRightUpDownButtonsForToolbar() { if (userX == 0) { leftTool.setEnabled(false); } else { leftTool.setEnabled(true); } //should be (user + 1) + (display - 1), the +1 and -1 can be taken out if (userX + numberOfColumnsToDisplay + 1 > getTotalNumberOfColumns()) { rightTool.setEnabled(false); } else { rightTool.setEnabled(true); } if (userY == 0) { upTool.setEnabled(false); } else { upTool.setEnabled(true); } if (userY + numberOfRowsToDisplay + 1 > getTotalNumberOfRows()) { downTool.setEnabled(false); } else { downTool.setEnabled(true); } } /** * Makes the image for the location at row, col. row and col start from 0. * The image will have a size of imageWidth x imageHeight. * * @param imageWidth * int of pixels of width of image * @param imageWidth * int of pixels of height of image * @param row * int of row to make image at, starting at 0 * @param col * int of column to make image at, starting at 0 * @return Image of size imageWidth * imageHeight */ protected Image makeImage(int imageWidth, int imageHeight, int row, int col, float scale, PageMargins margins) { Image image = new Image(shell.getDisplay(), imageWidth, imageHeight); GC gc = null; //check for rtl orientation... if ((shell.getStyle() & SWT.MIRRORED) != 0) { gc = new GC(image, SWT.RIGHT_TO_LEFT); } else gc = new GC(image); SWTGraphics sg = new SWTGraphics(gc); //for scaling ScaledGraphics g1 = new RenderedScaledGraphics(sg); //for himetrics and svg MapModeGraphics mmg = createMapModeGraphics(g1); //if mmg's font is null, gc.setFont will use a default font gc.setFont(mmg.getFont()); drawPage(mmg, gc, scale, row, col, margins); gc.dispose(); return image; } /** * Draw page to ScaledSWTGraphics which should have been initialized on a gc * from an image. * * @param g * the ScaledSWTGraphics we are drawing to. * @param gc * the GC that has a font set in it. * @param scale * how much we should scale by to fit a page in the image * @param row * using this value, we calculate how much to translate by. This * is not the row according to the page breaks on the diagram, * but it is the row according to the number of pages we are * displaying in the print preview. * @param col * using this value, we calculate how much to translate by. This * is not the col according to the page breaks on the diagram, * but it is the col according to the number of pages we are * displaying in the print preview. */ protected void drawPage(Graphics g, GC gc, float scale, int row, int col, PageMargins margins) { org.eclipse.draw2d.geometry.Point pageSize = PageInfoHelper .getPageSize(getPreferenceStore(), false, getMapMode()); g.pushState(); Rectangle bounds = getBounds(); if (PrintHelperUtil.getScaleToWidth() == 1 && PrintHelperUtil.getScaleToHeight() == 1 && fitToPage) { bounds = getDiagramEditPart().getChildrenBounds(); } int scaledPageSizeWidth = (int)(pageSize.x/userScale) ; int scaledPageSizeHeight = (int)(pageSize.y/userScale) ; //offset by page break figure bounds, then offset by the row or column we're at, and then take margins into account int translateX = - bounds.x - (scaledPageSizeWidth * (col + userX)) + (margins.left * (col + userX + 1)) + (margins.right * (col + userX)); int translateY = - bounds.y - (scaledPageSizeHeight * (row + userY)) + (margins.top * (row + userY + 1)) + (margins.bottom * (row + userY)); //To set a specific font, we could do this //For a completely accurate print preview, the font is printer specific //and may not be supported by the screen, so it is pointless //FontData fontData = JFaceResources.getDefaultFont().getFontData()[0]; //Font font = new Font(device, fontData); //g.setFont(font); if (doesPageExist(1 + userX + col, 1 + userY + row)) { g.pushState(); //draw text, don't make it too small or big g.scale(scale); String headerOrFooter = HeaderAndFooterHelper.makeHeaderOrFooterString( WorkspaceViewerProperties.HEADER_PREFIX, 1 + userY + row, 1 + userX + col, getDiagramEditPart()); g.drawText( headerOrFooter, (pageSize.x - getMapMode().DPtoLP(gc.textExtent(headerOrFooter).x)) / 2, getMapMode().DPtoLP(HeaderAndFooterHelper.TOP_MARGIN_DP)); headerOrFooter = HeaderAndFooterHelper.makeHeaderOrFooterString( WorkspaceViewerProperties.FOOTER_PREFIX, 1 + userY + row, 1 + userX + col, getDiagramEditPart()); g.drawText( headerOrFooter, (pageSize.x - getMapMode().DPtoLP(gc.textExtent(headerOrFooter).x)) / 2, pageSize.y - getMapMode().DPtoLP(HeaderAndFooterHelper.BOTTOM_MARGIN_DP)); g.popState(); //for drawing the text } g.scale(scale); g.translate(translateX, translateY); Rectangle clip = new Rectangle( (scaledPageSizeWidth - margins.left - margins.right) * (col + userX) + bounds.x, (scaledPageSizeHeight - margins.top - margins.bottom)* (row + userY) + bounds.y, scaledPageSizeWidth - margins.left - margins.right, scaledPageSizeHeight - margins.top - margins.bottom); g.clipRect(clip); //should be from printer and screen ratio and image size getDiagramEditPart().getLayer(LayerConstants.PRINTABLE_LAYERS).paint(g); g.popState(); } /** * Convenience method to determine if a page at row y and * column x exists. * Pages start at 1. * * For example, the first page is 1-1. * * @param x, column number of the page to check * @param y, row number of the page to check * * @return boolean true if the page exists, false if it doesn't */ private boolean doesPageExist(int x, int y) { return x > 0 && y > 0 && x <= getTotalNumberOfColumns() && y <= getTotalNumberOfRows(); } /** * Safely dispose an image * * @param image, the Image to dispose. */ private void safeDisposeImage(Image image) { if (image != null && !image.isDisposed()) image.dispose(); } /** * Dispose resources. */ protected void dispose() { disposeImages(); safeDisposeImage(printImage); safeDisposeImage(disabledPrintImage); safeDisposeImage(pageImage); safeDisposeImage(leftImage); safeDisposeImage(disabledLeftImage); safeDisposeImage(rightImage); safeDisposeImage(disabledRightImage); safeDisposeImage(upImage); safeDisposeImage(disabledUpImage); safeDisposeImage(downImage); safeDisposeImage(disabledDownImage); safeDisposeImage(closeImage); if (tempShell != null) { tempShell.dispose(); tempShell = null; } } /** * Convience method to get the workspcae preference store. * * @return IPreferenceStore */ private IPreferenceStore getWorkspaceViewerPreferenceStore() { assert getDiagramEditPart().getViewer() instanceof DiagramGraphicalViewer; return ((DiagramGraphicalViewer) getDiagramEditPart().getViewer()).getWorkspaceViewerPreferenceStore(); } private IPreferenceStore getGlobalPreferenceStore() { return (IPreferenceStore) getDiagramEditPart() .getDiagramPreferencesHint().getPreferenceStore(); } private IPreferenceStore getPreferenceStore() { if (getWorkspaceViewerPreferenceStore().getBoolean(WorkspaceViewerProperties.PREF_USE_WORKSPACE_SETTINGS)) return getGlobalPreferenceStore(); else return getWorkspaceViewerPreferenceStore(); } /** * Creates the <code>MapModeGraphics</code>. * * @param scaledGraphics * @return the <code>MapModeGraphics</code> */ protected MapModeGraphics createMapModeGraphics( ScaledGraphics scaledGraphics) { return new RenderedMapModeGraphics(scaledGraphics, getMapMode()); } /** * Sets the scale factor. * * @param scale : valid input is an integer larger than 0 representing a scale percentage */ private void setUserScale(int scale){ userScale = scale/100f; PrintHelperUtil.setScale(scale); } /** * Prepare a string appropriate to show the scale factor to the user. * * @param scale the scale factor, an integer greater than 0. * @return A string of the scale factor to be displayed to the user. */ private String getDisplayScale(int scale) { return String.valueOf(scale) + "%"; //$NON-NLS-1$ } /** * Determine the page count when fit to page is used. * * @param dgrmEP - The diagram edit part * @param figureBounds - The bounds of the figure * @param pageSize - Page size * @param applyUserScale - The user scale * @return Point.x contains the total number of pages that span in a column * Point.y contains the total number of pages that span in a row */ protected org.eclipse.draw2d.geometry.Point getPageCount( DiagramEditPart dgrmEP, Rectangle figureBounds, org.eclipse.draw2d.geometry.Point pageSize, boolean applyUserScale) { RootEditPart rootEditPart = dgrmEP.getRoot(); if (rootEditPart instanceof DiagramRootEditPart) { DiagramRootEditPart diagramRootEditPart = (DiagramRootEditPart) rootEditPart; PageBreakEditPart pageBreakEditPart = diagramRootEditPart .getPageBreakEditPart(); double fNumCols = ((PageBreaksFigure) pageBreakEditPart.getFigure()) .getPageCount().y * (applyUserScale ? userScale : 1); double fNumRows = ((PageBreaksFigure) pageBreakEditPart.getFigure()) .getPageCount().x * (applyUserScale ? userScale : 1); int numCols = (int) Math.ceil(fNumCols); int numRows = (int) Math.ceil(fNumRows); return new org.eclipse.draw2d.geometry.Point(numCols, numRows); } else { double fNumRows = (figureBounds.height * (applyUserScale ? userScale : 1)) / pageSize.y; int numRows = (int) Math.ceil(fNumRows); double fNumCols = (figureBounds.width * (applyUserScale ? userScale : 1)) / pageSize.x; int numCols = (int) Math.ceil(fNumCols); return new org.eclipse.draw2d.geometry.Point(numCols, numRows); } } /** * Reset the fit to page flag and set the user scale when the * preview is triggered from the print dialog. * * @param userScale a whole number greater than zero */ public void setPercentScaling(int userScale){ fitToPage = false; setUserScale(userScale); } /** * Recalculates a zoom ratio that can be used when displaying fit to page. * * @param rows The number of rows to fit the display to. * @param columns The number of columns to fit the display to. */ public void setFitToPage(int width, int height) { fitToPage = true; initializeMapMode(); Rectangle figureBounds = PrintHelperUtil.getPageBreakBounds( getDiagramEditPart(), true); org.eclipse.draw2d.geometry.Point pageBounds = PageInfoHelper .getPageSize(getPreferenceStore(), getMapMode()); org.eclipse.draw2d.geometry.Point pageCount = getPageCount( getDiagramEditPart(), figureBounds, pageBounds, false); int numCols = pageCount.x; int numRows = pageCount.y; float actualWidth = 0; float actualHeight = 0; if (height == 1 && width == 1) { figureBounds = getDiagramEditPart().getChildrenBounds(); actualWidth = figureBounds.width; actualHeight = figureBounds.height; } else { actualWidth = numCols * pageBounds.x; actualHeight = numRows * pageBounds.y; } int totalHeight = (height * pageBounds.y); int totalWidth = (width * pageBounds.x); int vScale = (int) ((totalHeight * 100) / actualHeight); int hScale = (int) ((totalWidth * 100) / actualWidth); userScale = Math.min(hScale, vScale)/100f; } /** * A dialog that prompts the user for scaling the print settings * to the number of pages wide and tall. * Scale to Pages will affect zoom and offsets. It will maximize the * scaling factor and modify offsets to fit the entire image within * the pages specified. * * @author James Bruck (jbruck) * */ private class FitToPagesDialog extends Dialog { /** * The text field that holds the pages width */ private Text textWide; /** * The text field that holds the pages height */ private Text textTall; /** * The number of pages wide */ private int pagesWide = PrintHelperUtil.getScaleToWidth(); /** * The number of pages tall */ private int pagesTall = PrintHelperUtil.getScaleToHeight(); public FitToPagesDialog(Shell parent) { super(parent); } /* * (non-Javadoc) * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) */ protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText(DiagramUIPrintingMessages.PrintPreview_FitToPage_DialogTitle); } /** * A helper that creates a label. * * @param parent * @param text * @return a newly created label. */ Label label(Composite parent, String text) { Label result = new Label(parent, SWT.NONE); result.setText(text); return result; } /** * A helper that gets layout data or creates * it as necessary. * * @param control * @return GridData for the layout */ GridData getLayoutData(Control control) { GridData result = (GridData) control.getLayoutData(); if (result == null) { result = new GridData(); control.setLayoutData(result); } return result; } /** * A helper to layout items horizontally. * * @param control * @param inset * @return The layed out control */ Control layoutHorizontalIndent(Control control, int inset) { GridData data = getLayoutData(control); data.horizontalIndent = inset; return control; } /** * A helper to right justify a control. * * @param control * @return The layed out control */ Control layoutAlignRight(Control control) { GridData data = getLayoutData(control); data.horizontalAlignment = SWT.END; data.grabExcessHorizontalSpace = false; return control; } /** * A helper to layout a certain number of dialog units wide. * @param control * @param dlus * @return the aligned out control */ Control layoutWidth(Control control, int dlus) { if (dlus > 0) { GridData data = getLayoutData(control); data.widthHint = convertHorizontalDLUsToPixels(dlus); } return control; } /** * A helper that lays out the given control horizontally. * * @param control * @param grab * @return The aligned control */ Control layoutFillHorizontal(Control control, boolean grab) { GridData data = getLayoutData(control); data.horizontalAlignment = SWT.FILL; data.grabExcessHorizontalSpace = grab; return control; } /** * A helper that creates a new text field. * * @param parent * @param width * @return a newly created text field */ Text text(Composite parent, int width) { Text result = new Text(parent, SWT.SINGLE | SWT.BORDER); layoutFillHorizontal(result, false); layoutWidth(result, width); return result; } /** * A helper that lays out a control grabbing both horizontal and * vertical extra spacing. * * @param control * @return */ Control layoutFillBoth(Control control) { GridData data = getLayoutData(control); data.horizontalAlignment = SWT.FILL; data.grabExcessHorizontalSpace = true; data.verticalAlignment = SWT.FILL; data.grabExcessVerticalSpace = true; return control; } /** * A helper that creates a new group. * * @param parent * @param text * @return A newly created group. */ Group group(Composite parent, String text) { Group result = new Group(parent, SWT.NONE); result.setText(text); layoutFillBoth(result); return result; } /** * Layout the given control with the number of columns specified. * * @param composite * @param columns * @return The aligned out control */ Composite layout(Composite composite, int columns) { GridLayout g = new GridLayout(columns, false); g.marginLeft = 6; g.marginRight = 6; g.marginTop = 6; g.marginBottom = 3; composite.setLayout(g); return composite; } /* * (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ protected Control createDialogArea(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); layout(composite, 1); Composite scaleGroup = group(composite, DiagramUIPrintingMessages.JPSPrintDialog_Scaling); layout(scaleGroup, 5); layoutHorizontalIndent(layoutAlignRight(label(scaleGroup, DiagramUIPrintingMessages.JPSPrintDialog_PagesWide)), 15); textWide = text(scaleGroup, 20); textWide.setText(String.valueOf(PrintHelperUtil.getScaleToWidth())); layoutHorizontalIndent(layoutAlignRight(label(scaleGroup, DiagramUIPrintingMessages.JPSPrintDialog_PagesTall)), 15); textTall = text(scaleGroup, 20); textTall .setText(String.valueOf(PrintHelperUtil.getScaleToHeight())); return composite; } public int getPagesWide() { return pagesWide; } public int getPagesTall() { return pagesTall; } /* * (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#okPressed() */ @Override protected void okPressed() { try { pagesWide = Integer.parseInt(textWide.getText()); } catch (NumberFormatException e) { // Ignore invalid entry; default is last known acceptable value } if(pagesWide < 1) { pagesWide = 1; } try { pagesTall = Integer.parseInt(textTall.getText()); } catch (NumberFormatException e) { // Ignore invalid entry; default is last known acceptable value } if(pagesTall < 1){ pagesTall = 1; } super.okPressed(); } } }